package ait.mediabroadcast.player;

import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;

import org.teleal.cling.android.AndroidUpnpService;
import org.teleal.cling.controlpoint.ActionCallback;
import org.teleal.cling.model.action.ActionArgumentValue;
import org.teleal.cling.model.action.ActionInvocation;
import org.teleal.cling.model.message.UpnpResponse;
import org.teleal.cling.model.meta.Action;
import org.teleal.cling.model.meta.ActionArgument;
import org.teleal.cling.model.meta.LocalDevice;
import org.teleal.cling.model.meta.LocalService;
import org.teleal.cling.model.meta.Service;
import org.teleal.cling.model.types.UDAServiceType;

import ait.mediabroadcast.R;
import ait.mediabroadcast.facebook.FBActivity;
import ait.mediabroadcast.helper.DeviceRole;
import ait.mediabroadcast.helper.MyConstants;
import ait.mediabroadcast.helper.PlaybackStatus;
import ait.mediabroadcast.helper.UpnpServiceManager;
import ait.mediabroadcast.pauseresume.PauseResumeService;
import ait.mediabroadcast.pauseresume.SetActionInvocation;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnSeekCompleteListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.LinearLayout;
import android.widget.MediaController;

public class MyPlayer extends Activity implements OnPreparedListener, OnErrorListener,
		OnCompletionListener, OnSeekCompleteListener, OnVideoSizeChangedListener,
		OnInfoListener, SurfaceHolder.Callback, MediaController.MediaPlayerControl {

	private static final String LOGTAG = MyPlayer.class.toString();
	private final int DURATION_SHOWING_CONTROL = 8000;
	
	private MediaPlayer mPlayer;
	private MediaController mController;
	private SurfaceView mSurfaceView;
	private SurfaceHolder mHolder;
	private int mVideoWidth;
    private int mVideoHeight;
    private Display mDisplay;
    
    private Bundle extras;
    private String playURI;
    private String mediaTitle;
    
    private int mediaCurrentPosition = 0;
    
    private AndroidUpnpService mUpnpService;
    private LocalDevice mLocalDevice;
    
    private boolean isPrepared = false;
    private Activity mActivity;
    private Intent mIntent;
    
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.myplayer);
	}
	
	@Override
	protected void onPause() {
		Log.d(LOGTAG, "onPause");
		super.onPause();
		pause();
	}

	@Override
	protected void onResume() {
		super.onResume();

		if (mIntent == null) {
			mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
			mHolder = mSurfaceView.getHolder();
			mHolder.addCallback(this);
			mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

			mDisplay = getWindowManager().getDefaultDisplay();

			mPlayer = new MediaPlayer();
			mPlayer.setOnPreparedListener(this);
			mPlayer.setOnErrorListener(this);
			mPlayer.setOnCompletionListener(this);
			mPlayer.setOnSeekCompleteListener(this);
			mPlayer.setOnVideoSizeChangedListener(this);

			mController = new MediaController(this);

			extras = getIntent().getExtras();
			if (extras != null) {
				playURI = extras.getString(MyConstants.PLAY_URI);
				mediaTitle = extras.getString(MyConstants.MEDIA_TITLE);
				if (extras.getInt(MyConstants.PAUSE_TIME) != 0 && !isPrepared)
					mediaCurrentPosition = extras.getInt(MyConstants.PAUSE_TIME); 
			}

			mActivity = (Activity) this;
			try {
				mPlayer.setDataSource(playURI);
			} catch (IllegalArgumentException e) {
				Log.e(LOGTAG, e.getMessage());
				finish();
			} catch (IllegalStateException e) {
				Log.e(LOGTAG, e.getMessage());
				finish();
			} catch (IOException e) {
				Log.e(LOGTAG, e.getMessage());
				finish();
			} catch (Exception e) {
				Log.e(LOGTAG, e.getMessage());
				finish();
			}
		} else {
			/* probably we can give dialog saying that the server "pauses" the media playback
			 * and offer to continue playing or not
			 */
			
			if (mIntent.getAction().equals(MyConstants.PAUSE_STRING)) {
				AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivity);
				alertDialogBuilder.setMessage(getString(R.string.dialog_ask_pause_message))
						.setTitle(getString(R.string.dialog_ask_pause_title));
				alertDialogBuilder.setNegativeButton("No", new OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						// Do nothing!
						mPlayer.pause();
					}
				});
				alertDialogBuilder.setPositiveButton("Yes", new OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						mPlayer.start();
					}
				});
				AlertDialog alertDialog = alertDialogBuilder.create();
				alertDialog.show();
			}
		}
		
	}
	
	// do we need onStop()?
	
	@Override
	protected void onDestroy() {
		Log.d(LOGTAG, "myPlayer onDestroy");
		super.onDestroy();
		if (mPlayer != null) {
			mPlayer.release();
			mPlayer = null;
		}
	}
	
	@Override
	protected void onNewIntent(Intent intent) {
		super.onNewIntent(intent);
		mIntent = intent;
	}
	
	@Override
	public void onPrepared(MediaPlayer mp) {
		Log.d(LOGTAG, "onPrepared");
		
		isPrepared = true;
		
		mVideoHeight = mp.getVideoHeight();
		mVideoWidth = mp.getVideoWidth();
		
		// check video size and display, scale if needed
		if (mVideoHeight > mDisplay.getHeight() || mVideoWidth > mDisplay.getWidth()) {
			float ratioHeight = (float) mDisplay.getHeight() / (float) mVideoHeight;
			float ratioWidth = (float) mDisplay.getWidth() / (float) mVideoWidth;
			if (ratioHeight < ratioWidth) {
				mVideoHeight = (int) Math.floor((float) mVideoHeight * ratioHeight);
				mVideoWidth = (int) Math.floor((float) mVideoWidth * ratioHeight);
			} else {
				mVideoHeight = (int) Math.floor((float) mVideoHeight * ratioWidth);
				mVideoWidth = (int) Math.floor((float) mVideoWidth * ratioWidth);
			}
		}
		
		mSurfaceView.setLayoutParams(new LinearLayout.LayoutParams(mVideoWidth, mVideoHeight));
			
		if (mediaCurrentPosition != 0)
			mPlayer.seekTo(mediaCurrentPosition);
		mPlayer.start();
		
		mController.setMediaPlayer(this);
		mController.setAnchorView(mSurfaceView);
		mController.setEnabled(true);
		mController.show(DURATION_SHOWING_CONTROL);
	}

	@Override
	public boolean onError(MediaPlayer mp, int what, int extra) {
		Log.v(LOGTAG, "onError Called");
		if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
			Log.d(LOGTAG, "Media Error, Server Died " + extra);
		} else if (what == MediaPlayer.MEDIA_ERROR_UNKNOWN) {
			Log.d(LOGTAG, "Media Error, Error Unknown " + extra);
		}
		return false;
	}

	@Override
	public void onCompletion(MediaPlayer mp) {
		Log.d(LOGTAG, "onCompletion");
		mp.reset();
		finish();
	}

	@Override
	public void onSeekComplete(MediaPlayer mp) {
		Log.d(LOGTAG, "onSeekComplete");
	}

	@Override
	public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
		Log.d(LOGTAG, "onVideoSizeChanged");
	}

	@Override
	public boolean onInfo(MediaPlayer mp, int what, int extra) {
		if (what == MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING) {
			Log.d(LOGTAG, "Media Info, Media Info Bad Interleaving " + extra);
		} else if (what == MediaPlayer.MEDIA_INFO_NOT_SEEKABLE) {
			Log.d(LOGTAG, "Media Info, Media Info Not Seekable " + extra);
		} else if (what == MediaPlayer.MEDIA_INFO_UNKNOWN) {
			Log.d(LOGTAG, "Media Info, Media Info Unknown " + extra);
		} else if (what == MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING) {
			Log.d(LOGTAG, "MediaInfo, Media Info Video Track Lagging " + extra);
		} else if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
			Log.d(LOGTAG, "MediaInfo, Media Info Metadata Update " + extra);
		}
		return false;
	}

	/* SurfaceHolder callback */
	
	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		mPlayer.setDisplay(mHolder);
		try {
			mPlayer.prepareAsync();
		} catch (IllegalStateException e) {
			Log.e(LOGTAG, e.getMessage());
			finish();
		} catch (Exception e) {
			Log.e(LOGTAG, "onSurfaceCreated " + e.getMessage());
			finish();
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		Log.d(LOGTAG, "surfaceChanged");
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		Log.d(LOGTAG, "surfaceDestroyed");
	}
	
	/* MediaController.MediaPlayer callback */
	
	@Override
	public void start() {
		mPlayer.start();
	}

	@Override
	public void pause() {
		if (isPrepared) {
			mPlayer.pause();
			mediaCurrentPosition = mPlayer.getCurrentPosition();
			if (PlaybackStatus.getDeviceRole() == DeviceRole.SERVER_DEVICE) {
				sendPauseSignal(mediaCurrentPosition);
				resetServiceServerAndStatus();
			} 
			PlaybackStatus.resetBroadcastServer();
		}
	}

	@Override
	public int getDuration() {
		if (mPlayer == null)
			return 0;
		return mPlayer.getDuration();
	}

	@Override
	public int getCurrentPosition() {
		if (mPlayer == null)
			return 0;
		return mPlayer.getCurrentPosition();
	}

	@Override
	public void seekTo(int pos) {
		mPlayer.seekTo(pos);
	}

	@Override
	public boolean isPlaying() {
		if (mPlayer == null)
			return false;
		return mPlayer.isPlaying();
	}

	@Override
	public int getBufferPercentage() {
		return 0;
	}

	@Override
	public boolean canPause() {
		return true;
	}

	@Override
	public boolean canSeekBackward() {
		return true;
	}

	@Override
	public boolean canSeekForward() {
		return true;
	}

	@Override
	public int getAudioSessionId() {
		return mPlayer.getAudioSessionId();
	}
	
	/* other callbacks */
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (mController.isShowing()) {
			mController.hide();
		} else {
			mController.show(DURATION_SHOWING_CONTROL);
		}
		return super.onTouchEvent(event);
	}
	
	/* own methods */
	
	private void sendPauseSignal(int currentPosition) {
		/*
		 * get local service
		 * set the status to playURI & time
		 */
		mUpnpService = getUpnpService();
		mLocalDevice = getLocalDevice();
		LocalService<PauseResumeService> prService = getLocalService();
		
		Action action = prService.getAction(MyConstants.ACTION_SET_TARGET);
		String newTargetValue = MyConstants.PAUSE_STRING + MyConstants.DELIMITER + playURI + 
				MyConstants.DELIMITER + currentPosition
				+ MyConstants.DELIMITER + mediaTitle; 
		
		ActionArgument argument = action.getInputArgument(MyConstants.ARGUMENT_NEW_TARGET);
		ActionArgumentValue aargv = new ActionArgumentValue(argument, newTargetValue);
		ActionArgumentValue aargvArray [] = {aargv};
		SetActionInvocation actInvocation = new SetActionInvocation(action, aargvArray);

		ActionCallback callback = new ActionCallback(actInvocation) {
			
			@Override
			public void success(ActionInvocation invocation) {
				Log.d(LOGTAG, "success");
			}
			
			@Override
			public void failure(ActionInvocation invocation, UpnpResponse operation,
					String defaultMsg) {
				Log.e(LOGTAG, "failure");
			}
		};
		mUpnpService.getControlPoint().execute(callback);
	}
	
	private LocalService<PauseResumeService> getLocalService() {
		Service service = mLocalDevice.findService(
				new UDAServiceType(MyConstants.PAUSE_RESUME_SERVICE, 1));
		LocalService<PauseResumeService> prService = (LocalService<PauseResumeService>) service;
		return prService;
	}
	
	private LocalDevice getLocalDevice() {
		if (mLocalDevice == null) {
			Collection<LocalDevice> localDevices = mUpnpService.getRegistry().getLocalDevices();
			if (localDevices.size() > 0) {
				Iterator it = localDevices.iterator();
				mLocalDevice = (LocalDevice) it.next();
			}
		}
		return mLocalDevice;
	}
	
	private AndroidUpnpService getUpnpService() {
		if (mUpnpService == null) {
			mUpnpService = UpnpServiceManager.getInstance();
		}
		return mUpnpService;
	}
	
	private void resetServiceServerAndStatus() {
		mUpnpService = getUpnpService();
		mLocalDevice = getLocalDevice();
		LocalService<PauseResumeService> prService = getLocalService();
		
		String [] actionsToReset = {MyConstants.ACTION_SET_SERVER, MyConstants.ACTION_SET_TARGET};
		String [] argumentsToReset = {MyConstants.ARGUMENT_NEW_SERVER, MyConstants.ARGUMENT_NEW_TARGET};
		
		for (int i = 0; i < 2; i++) {
			Action action = prService.getAction(actionsToReset[i]);
			String newServerValue = MyConstants.FALSE_STRING; 
			
			ActionArgument argument = action.getInputArgument(argumentsToReset[i]);
			ActionArgumentValue aargv = new ActionArgumentValue(argument, newServerValue);
			ActionArgumentValue aargvArray [] = {aargv};
			SetActionInvocation actInvocation = new SetActionInvocation(action, aargvArray);

			ActionCallback callback = new ActionCallback(actInvocation) {
				
				@Override
				public void success(ActionInvocation invocation) {
					Log.d(LOGTAG, "success");
				}
				
				@Override
				public void failure(ActionInvocation invocation, UpnpResponse operation,
						String defaultMsg) {
					Log.e(LOGTAG, "failure");
				}
			};
			mUpnpService.getControlPoint().execute(callback);
		}
		
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		menu.add(0, 0, 0, getString(R.string.share_facebook));
		return true;
	}	
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		switch (item.getItemId()) {

			case 0:
	//-->v
//				String urlAndTime = "TEST::TEST::RECEIVED_FB_STATUS";
				Intent myIntent = new Intent();
				myIntent.setClass(MyPlayer.this, FBActivity.class);
				myIntent.putExtra(MyConstants.MEDIA_TITLE, mediaTitle);
				startActivity(myIntent);
				break;
	//<--v
		}
		return false;
		}	
	
}
